﻿STD.Control = Class.extend({
    INVALID: "INVALID",
    CLASS_SIGNATURE: "Control",
    _throwReadOnlyError: function (property) {
        new Error(this.CLASS_SIGNATURE + ": Sorry [" + property + "] is a read-only property")
    },
   /* @Function:   _assignEvents
    * @Purpose:    assign a specified group (of events) to target
    * @Parameters: group:string - group name
    *              target:{string|DOM|$} - target need to bind
    * @Return:     none
    * @Scope:      STD.Control
    */
    _assignEvents: function (group, target) {
        var key, handlers;
        var instance = this;
        this._activeEvents = this._activeEvents || {};
        if (typeof target === "string") {
            key = group + "/" + target;
            if (this._activeEvents[key]) {
                return
            }
            handlers = this._activeEvents[key] = {};
            STD.$.each(this._events[group], function (eventType, handler) {
                handlers[eventType] = function (event) {
                    handler.call(instance, event, STD.$(this))
                };
                STD.$(document).delegate(target, eventType, handlers[eventType])
            })
        } else {
            target = STD.$(target);
            if (target.length === 0) {
                return
            }
            if (this._activeEvents[group + "/" + target[0][STD.$.expando]]) {
                return
            }
            handlers = {};
            STD.$.each(this._events[group], function (eventType, handler) {
                handlers[eventType] = function (event) {
                    handler.call(instance, event, STD.$(this))
                };
                target.bind(eventType, handlers[eventType])
            });
            key = group + "/" + (target[0] === window ? "window" : target[0][STD.$.expando]);
            this._activeEvents[key] = handlers
        }
    },
    _unassignEvents: function (group, target) {
        var key, handlers, eventType;
        this._activeEvents = this._activeEvents || {};
        if (typeof target === "string") {
            key = group + "/" + target;
            handlers = this._activeEvents[key];
            if (!handlers) {
                return
            }
            for (eventType in handlers) {
                STD.$(document).undelegate(target, eventType, handlers[eventType])
            }
        } else {
            target = STD.$(target);
            if (target.length === 0) {
                return
            }
            key = group + "/" + target[0][STD.$.expando];
            handlers = this._activeEvents[key];
            if (!handlers) {
                return
            }
            try {
                for (eventType in handlers) {
                    target.unbind(eventType, handlers[eventType])
                }
            } catch (error) {
                var events = STD.$.data(target[0], "events");
                if (events) {
                    for (eventType in handlers) {
                        var $handlers = events[eventType];
                        if (!$handlers) {
                            continue
                        }
                        var i = $handlers.length;
                        while (i--) {
                            if ($handlers[i].handler === handlers[eventType]) {
                                $handlers.splice(i, 1);
                                if ($handlers.length === 0) {
                                    delete events[eventType]
                                }
                                break
                            }
                        }
                    }
                }
            }
        }
        delete this._activeEvents[key]
    },
    _isValidInput: function () {
        return true
    },
    _handleKeyEvent: function (e) {
        var instance = this;
        if (instance._isValidInput(e)) {
            var SpecialKey = JIRA.Keyboard.SpecialKey,
                shortcut = JIRA.Keyboard.shortcutEntered(e);
            if (shortcut) {
                if (instance.keys[shortcut]) {
                    instance.keys[shortcut].call(instance, e);
                    return
                } else {
                    if ((shortcut === SpecialKey.BACKSPACE || shortcut === SpecialKey.DELETE) && instance.keys.onEdit) {
                        instance.keys.onEdit.call(instance, e);
                        return
                    }
                }
            }
            var character = JIRA.Keyboard.characterEntered(e);
            if (character && instance.keys.onEdit) {
                instance.keys.onEdit.call(instance, e, character)
            }
        }
    },
    getCustomEventName: function (methodName) {
        return (this.CLASS_SIGNATURE || "") + "_" + methodName
    },
    _getCustomEventArgs: function () {
        return [this]
    },
    trigger: function (event) {
        return STD.trigger(event, this)
    },
    _supportsBoxShadow: function () {
        var s = document.body.style;
        return s.WebkitBoxShadow !== undefined || s.MozBoxShadow !== undefined || s.boxShadow !== undefined
    },
    _setOptions: function (options) {
        var element, optionsFromDOM;
        options = options || {};
        if (options instanceof STD.$ || typeof options === "string" || (typeof options === "object" && options.nodeName)) {
            options = {
                element: options
            }
        }
        element = STD.$(options.element);
        optionsFromDOM = element.getOptionsFromAttributes();
        this.options = STD.$.extend(true, this._getDefaultOptions(options), optionsFromDOM, options);
        if (element.length === 0) {
            return this.INVALID
        }
        return undefined
    },
    getCaret: function (node) {
        var startIndex = node.selectionStart;
        if (startIndex >= 0) {
            return (node.selectionEnd > startIndex) ? -1 : startIndex
        }
        if (document.selection) {
            var textRange1 = document.selection.createRange();
            if (textRange1.text.length === 0) {
                var textRange2 = textRange1.duplicate();
                textRange2.moveToElementText(node);
                textRange2.setEndPoint("EndToStart", textRange1);
                return textRange2.text.length
            }
        }
        return -1
    },
    _render: function () {
        var i, name = arguments[0],
            args = [];
        for (i = 1; i < arguments.length; i++) {
            args.push(arguments[i])
        }
        return this._renders[name].apply(this, args)
    }
});

/* Constant Enum: STD.Position
 * Scope: STD
 */
STD.Position = {
    LEFT: 1,
    RIGHT: 2,
    TOP: 4,
    BOTTOM: 8
};

/* @Function:   IsUrl
 * @Purpose:    Determine whether the string is URL
 * @Parameters: str:string - the string you want to determine
 * @Return:     if the string is URL, return true, else return false
 * @Scope:      STD
 */
STD.IsUrl = function (str) {
	if(typeof str != 'string') {
		return false;
	}else{
		return /([\w-]+\.)+[\w-]+.([^a-z])(\/[\w-: .\/?%&=]*)?|[a-zA-Z\-\.][\w-]+.([^a-z])(\/[\w-: .\/?%&=]*)?/gi.test(str);
	}   
};


STD.Layer = STD.Control.extend({
	options: {position: STD.Position.LEFT|STD.Position.BOTTOM},
    isShow: false,
	/* @Function:   init
	 * @Purpose:    STD.Layer construct function
	 * @Parameters: options[layer]: {
	 *		container[string]: 	[required], the container of the layer
	 *		content[string]:	[required], element id or ajax url, specify the content need to be show
	 *		position[enum]:		[required], default to LEFT|BOTTOM
	 *		}			
	 *
	 * @Return:     none
	 * @Scope:      STD.Layer
	 * [public]
	 */
    init: function (options) {
        this.options = STD.copyObject(STD.$.extend(this.options, options));
        this.container = $(this.options.container);
        this.placeholder = this._render('placeholder').appendTo(this.container);
		if(!STD.IsUrl(this.options.content)) this._assignEvents('stopPropagation', $(this.options.content).appendTo(this.placeholder));
    },
	/* @Function:   hide
	 * @Purpose:    hide the layer and append cotent to container
	 * @Parameters: none
	 * @Return:     none
	 * @Scope:      STD.Layer
	 * [public]
	 */
    hide: function () {
		if(this.isShow === false) return;
		this.content.appendTo(this.placeholder);
        this.isShow = false;
    },
	/* @Function:   show
	 * @Purpose:    append the cotent to body and show
	 * @Parameters: none
	 * @Return:     none
	 * @Scope:      STD.Layer
	 * [public]
	 */
    show: function () {
		if(this.isShow === true) return;
		this.content = this.placeholder.contents();
		if(this.content.length === 0) {
			this.getAjaxContent();
			return;
		}
		var offset = this.container.offset();
		var $content = this.content;
		var pos = this.setPosition({
				width: $content.outerWidth(), 
				height: $content.outerHeight()
			},
			{
				x: offset.left,
				y: offset.top,
				width: this.container.outerWidth(),
				height: this.container.outerHeight()
			},
			this.options.position
		);
		$content.css({ display: "block", left: pos.x, top: pos.y, width: $content.width() > 200 ? $content.width() : 200 })
		$content.appendTo('body');
        this.isShow = true;
    },
	/* @Function:   getAjaxContent
	 * @Purpose:    get the content from ajax and show the cotent
	 * @Parameters: none
	 * @Return:     none
	 * @Scope:      STD.Layer
	 * [public]
	 */
	getAjaxContent: function () {
		var that = this;
		$.ajax({
			"url": this.options.content,
			"type": "post",
			"success": function (content) {
				that._assignEvents('stopPropagation', $(content).appendTo(that.placeholder));
				that.show();
            },
            "dataType": "html",
            "cache": false
        });
	},
	/* @Function:   setPosition
	 * @Purpose:    set the corresponding positon of the cotent to container
	 * @Parameters:
	 *			size: {
	 *			width[number]: width of the content,
	 *			height[number]: height of the content
	 *			}
	 *			coor - {
	 *			x: the value of X coordinate
	 *			y: the value of Y coordinate
	 *			height: the height of the container
	 *			width: the width of the container
	 *			}
	 *			align[STD.Position]: LEFT, RIGHT, TOP, BOTTOM
	 * @Return:     none
	 * @Scope:      STD.Layer
	 * [private]
	 */
    setPosition: function (size, coor, align) {
		var r = {x: 0, y: 0};
		switch(align) {
			case STD.Position.LEFT|STD.Position.BOTTOM: r.x = coor.x, r.y = coor.y + coor.height;break;
			case STD.Position.RIGHT|STD.Position.BOTTOM: r.x = coor.x + (coor.width - size.width), r.y = coor.y + coor.height;break;
			default: r.x = -1; r.y = -1; break;
		}		
		return r;
    },
    _renders: {
        placeholder: function () {
            return STD.$('<div />').addClass('std-layer-placeholder');
        }
    },
	_events: {
        'stopPropagation': { 'click': function (e) {
			e.stopPropagation();}
		}
    }
});

STD.Dropdown = STD.Control.extend({
    hideAll: function () { },
    init: function (options) {
        this.options = STD.copyObject(STD.$.extend(this.options, options));
        this.layer = new STD.Layer(this.options.layer);
		this.trigger = $(this.options.trigger);
        this._assignEvents('dropdown', this.trigger);
    },
	hide: function () {
		this.layer.hide();
	},
    _events: {
        'dropdown': { 'click': function (e) {
            e.preventDefault();
            e.stopPropagation();
            if (this.layer.isShow) {
                this.layer.hide();
            } else {
                this.hideAll();
                this.layer.show();
            }
        }
        }
    }
});

STD.DropdownController = STD.Control.extend({
	init: function () {
		this.dropdowns = [];	
		this._assignEvents('hideOnClick', jQuery(document.body));
	},
	add: function (dropdown) {
		dropdown.hideAll = this.hideAll.bind(this);
		this.dropdowns.push(dropdown);
	},
	hideAll: function () {
		for(var item in this.dropdowns) {
			this.dropdowns[item].hide();
		}
	},
	_events: {
        'hideOnClick': { 'click': function (e) {
			this.hideAll();}
        }
    }
});


